package errors

import (
	"encoding/json"
	"errors"
	"fmt"
)

const (
	ZONE_SIZE = 100
)

const (
	ERROR_UNKNOWN_ERROR = 90000 + iota

	ERR_VALIDATE_SCHEMA_ERROR
	ERR_NO_SUCH_SCHEMA
	ERR_NO_SUCH_SERVER
	ERR_NO_SUCH_PATH
	ERR_NO_SUCH_METHOD
	ERROR_UNKNOWN_METHOD //未知的方法

	ERROR_CALLING_NOT_CORRECT_RESPONSE
	ERROR_CALLING_NO_RESPONSE_DEFINED
	ERROR_NO_SERVER_SELECTED

	ERROR_FORBIDDEN
)

func (e *Error) Error() string {
	return printErr(e.Code, e.Cause)
}

func (e *Error) RespondAsJson() bool {
	return true
}

type jsonError struct {
	Code    int
	Cause   string
	Message string
}

func (n *Error) UnmarshalJSON(bts []byte) error {
	var j jsonError
	err := json.Unmarshal(bts, &j)
	if err == nil {
		n.Code = j.Code
		if j.Cause != "" {
			n.Cause = errors.New(j.Cause)
		}
	}
	return err
}

func (n *Error) MarshalJSON() ([]byte, error) {
	var j jsonError
	j.Code = n.Code
	if n.Cause != nil {
		j.Cause = n.Cause.Error()
	}
	j.Message = n.Error()
	return json.Marshal(&j)
}

func printErr(code int, cause error) string {
	if cause != nil {
		return fmt.Sprintf("[%d]%s:%s", code, getDefaultMessage(code), cause.Error())
	} else {
		return fmt.Sprintf("[%d]%s", code, getDefaultMessage(code))
	}
}

func getDefaultMessage(code int) string {
	switch code {
	case ERR_NO_SUCH_SCHEMA:
		return "对应定义为空，请检查相关配置"
	case ERROR_UNKNOWN_ERROR:
		return "未知系统错误，请联系管理员或重试"
	case ERR_NO_SUCH_PATH:
		return "没有这个路径"
	case ERR_NO_SUCH_METHOD:
		return "没有这个方法"
	case ERROR_CALLING_NO_RESPONSE_DEFINED:
		return "没有定义返回值格式"
	case ERROR_NO_SERVER_SELECTED:
		return "没有选择目标服务器或提供服务器地址"
	case ERROR_UNKNOWN_METHOD:
		return "未知的调用方法或调用方法不正确"
	}
	zone := code / ZONE_SIZE
	if f, ok := knownErrHandler[zone]; ok {
		var str = f(code)
		if str != "" {
			return str
		}
	}
	return "未知错误"
}

type Error struct {
	Code  int
	Cause error
}

func newCause(msgs ...interface{}) error {
	if len(msgs) > 0 {
		switch tm := msgs[0].(type) {
		case string:
			return errors.New(tm)
		case error:
			return tm
		}
	}
	return nil
}

func New(code int, msgs ...interface{}) *Error {
	var err Error
	err.Code = code
	err.Cause = newCause(msgs...)
	return &err
}

type errPrtFns func(int) string

var knownErrHandler = make(map[int]errPrtFns)

func RegMessage(zone int, fn errPrtFns) {
	knownErrHandler[zone/ZONE_SIZE] = fn
}

func Own(err error) bool {
	_, ok := err.(*Error)
	return ok
}

func Code(err error) int {
	myE, ok := err.(*Error)
	if ok {
		return myE.Code
	}
	return -1
}
